package com.pronosticnews.server.servlet;

import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import javax.imageio.ImageIO;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.pronosticnews.server.domain.model.Image;
import com.pronosticnews.server.services.images.ImageService;

import fr.ysa.persistence.requetes.ConfigRequetes;
import fr.ysa.persistence.requetes.filtres.Filtre;
import fr.ysa.persistence.requetes.filtres.Filtre.TYPE;
import fr.ysa.persistence.requetes.filtres.ValueFiltre;
import fr.ysa.persistence.requetes.filtres.ValueFiltre.OPERATION;

public class ImageServlet extends HttpServlet {

	private static final long serialVersionUID = 6214634721115949600L;

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {

		ServletContext servletContext = this.getServletContext();
		WebApplicationContext wac = WebApplicationContextUtils
				.getRequiredWebApplicationContext(servletContext);
		ImageService imageService = (ImageService) wac.getBean("imageService");

		// On vérifie que le formulaire est bien de type multipart
		if (ServletFileUpload.isMultipartContent(req)) {
			ServletFileUpload servletFileUpload = new ServletFileUpload(
					new DiskFileItemFactory());
			try {
				List<FileItem> fileItems = servletFileUpload.parseRequest(req);
				byte[] contenu = null;
				byte[] contenu_mini = null;
				String type = "";
				Integer idImage = null;

				for (FileItem fileItem : fileItems) {
					// Le champs contenant le fichier
					if (!fileItem.isFormField()) {
						type = fileItem.getContentType();

						contenu = IOUtils
								.toByteArray(fileItem.getInputStream());
						BufferedImage bufferedImage = ImageIO.read(fileItem
								.getInputStream());

						double factor = getReduceFactor(bufferedImage);

						if (factor > 0) {
							String[] splitType = type.split("/");
							String typeImage = splitType[1];

							BufferedImage bufferedReducedImage = getScaledInstance(
									bufferedImage, factor, true);
							ByteArrayOutputStream baos = new ByteArrayOutputStream();

							ImageIO.write(bufferedReducedImage, typeImage, baos);
							contenu_mini = baos.toByteArray();
						} else {
							contenu_mini = contenu;
						}
					} else if ("idImage".equals(fileItem.getName())) {
						if (fileItem.getString() != null
								&& !"".equals(fileItem.getString())) {
							idImage = Integer.parseInt(fileItem.getString());
						}
					}
				}
				// Message d'erreur si tous les champs n'ont pas été remplie
				if (contenu == null) {
					resp.getOutputStream().print(
							"Tous les champs n'ont pas été remplis");
					return;
				}
				Image image = new Image();
				image.setType(type);
				image.setContenu(contenu);
				image.setContenu_mini(contenu_mini);
				image.setId(idImage);
				Image imageSaved = null;
				if (idImage != null) {
					imageSaved = imageService.modifier(image);
				} else {
					imageSaved = imageService.sauvegarder(image);
				}
				resp.setContentType("text/plain");
				resp.getOutputStream().print(imageSaved.getId().toString());
			} catch (Exception e) {
				System.out.println(e);
				resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
						e.getLocalizedMessage());
			}
		}
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		ServletContext servletContext = this.getServletContext();
		WebApplicationContext wac = WebApplicationContextUtils
				.getRequiredWebApplicationContext(servletContext);
		ImageService imageService = (ImageService) wac.getBean("imageService");
		String cle = req.getParameter(Image.VALUE_NAME.id.name());
		Integer maxWidth = null;
		Integer maxHeight = null;
		if (req.getParameter("width") != null) {
			maxWidth = Integer.parseInt(req.getParameter("width"));
		}
		if (req.getParameter("height") != null) {
			maxHeight = Integer.parseInt(req.getParameter("height"));
		}

		if (cle == null) {
			resp.sendError(HttpServletResponse.SC_BAD_REQUEST,
					"La clé de l'image est requise");
		} else {
			try {
				ConfigRequetes confReq = new ConfigRequetes();
				Filtre filtreIdImage = new Filtre();
				filtreIdImage.setType(TYPE.NUMBER);
				filtreIdImage.setName(Image.VALUE_NAME.id.name());
				ValueFiltre valueIdImage = new ValueFiltre();
				valueIdImage.setOperation(OPERATION.EQUAL);
				valueIdImage.setValue(cle);
				filtreIdImage.addValue(valueIdImage);
				confReq.getFiltres().add(filtreIdImage);

				List<Image> images = imageService.trouver(confReq);
				if (images.size() != 1) {
					resp.sendError(HttpServletResponse.SC_BAD_REQUEST,
							"Problem");
				}

				byte[] returnbyte = images.get(0).getContenu();

				double factor = 0;
				InputStream in = null;
				BufferedImage bImageFromConvert = null;

				if (maxWidth != null || maxHeight != null) {
					in = new ByteArrayInputStream(returnbyte);
					bImageFromConvert = ImageIO.read(in);
				}

				if (maxWidth != null && maxHeight != null) {
					if (maxWidth < maxHeight) {
						factor = getReduceFactor(maxWidth,
								bImageFromConvert.getWidth());
					} else {
						factor = getReduceFactor(maxHeight,
								bImageFromConvert.getHeight());
					}
				} else {
					if (maxWidth != null) {
						factor = getReduceFactor(maxWidth,
								bImageFromConvert.getWidth());
					} else if (maxHeight != null) {
						factor = getReduceFactor(maxHeight,
								bImageFromConvert.getHeight());
					}
				}

				if (bImageFromConvert != null && factor != 0) {
					String[] splitType = images.get(0).getType().split("/");
					String typeImage = splitType[1];

					bImageFromConvert = getScaledInstance(bImageFromConvert,
							factor, true);
					ByteArrayOutputStream baos = new ByteArrayOutputStream();

					ImageIO.write(bImageFromConvert, typeImage, baos);
					returnbyte = null;
					returnbyte = baos.toByteArray();
				}

				resp.setContentType(images.get(0).getType());
				resp.getOutputStream().write(returnbyte);

			} catch (Exception e) {
				resp.sendError(HttpServletResponse.SC_NOT_FOUND,
						e.getLocalizedMessage());
			}
		}
	}

	private double getReduceFactor(int maxSize, int originalSize) {
		double factor = 0;
		factor = (double) maxSize / (double) originalSize;
		return factor;
	}

	private double getReduceFactor(BufferedImage bufferedImage) {
		double factor = 0;
		if (bufferedImage.getWidth() > Image.MIN_WIDTH_MINI) {
			factor = (double) Image.MIN_WIDTH_MINI
					/ (double) bufferedImage.getWidth();
		} else if (bufferedImage.getHeight() > Image.MIN_HEIGHT_MINI) {
			factor = (double) Image.MIN_HEIGHT_MINI
					/ (double) bufferedImage.getHeight();
		}
		return factor;
	}

	public BufferedImage getScaledInstance(BufferedImage img, double factor,
			boolean higherQuality) {

		int targetWidth = (int) (img.getWidth() * factor);
		int targetHeight = (int) (img.getHeight() * factor);

		int type = (img.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB
				: BufferedImage.TYPE_INT_ARGB;
		BufferedImage ret = (BufferedImage) img;
		int w, h;
		if (higherQuality) {
			// Use multi-step technique: start with original size, then
			// scale down in multiple passes with drawImage()
			// until the target size is reached
			w = img.getWidth();
			h = img.getHeight();
		} else {
			// Use one-step technique: scale directly from original
			// size to target size with a single drawImage() call
			w = targetWidth;
			h = targetHeight;
		}

		do {
			if (higherQuality && w > targetWidth) {
				w /= 2;
				if (w < targetWidth) {
					w = targetWidth;
				}
			}

			if (higherQuality && h > targetHeight) {
				h /= 2;
				if (h < targetHeight) {
					h = targetHeight;
				}
			}

			BufferedImage tmp = new BufferedImage(w, h, type);
			Graphics2D g2 = tmp.createGraphics();
			g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
					RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
			g2.drawImage(ret, 0, 0, w, h, null);
			g2.dispose();

			ret = tmp;
		} while (w != targetWidth || h != targetHeight);

		return ret;
	}

}
